package com.gaojinqi.sys.core.shiro.util;

import com.gaojinqi.base.common.cache.KvCache;
import com.gaojinqi.base.common.util.JsonUtil;
import com.gaojinqi.base.common.util.RequestResponseUtil;
import com.gaojinqi.base.common.vo.IResult;
import com.gaojinqi.base.common.vo.ResultVo;
import com.gaojinqi.base.core.util.HttpContext;
import com.gaojinqi.sys.common.constant.ShiroDefaultConst;
import com.gaojinqi.sys.config.properties.JwtProperties;
import com.gaojinqi.sys.modular.rbac.model.Permission;
import com.gaojinqi.sys.modular.rbac.model.Role;
import com.gaojinqi.sys.modular.rbac.service.PermissionService;
import com.gaojinqi.sys.modular.rbac.service.RoleService;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.*;

/**
 * Jwt token 工具类
 *
 * @author gaojinqi
 * @version 1.0
 * @since 2020年05月09日
 */
@Slf4j
@Component
public class JwtTokenHelper {

    /**
     * jwt 中 Claims中的角色 key
     */
    public final String ROLE_KEY = "roles";

    /**
     * jwt 中 Claims中的权限 key
     */
    public final String PERMISSION_KEY = "perms";

    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private KvCache<String, String> cache;

    /**
     * 创建jwt并放入缓存中
     */
    public String createAndCacheToken(String userName) {
        String key = this.getKey(userName);
        HashMap<String, Object> params = Maps.newHashMap();

        // 设置角色
        List<Role> roleList = roleService.findRolesByUserName(userName);
        Set<String> roleIdSet = Sets.newLinkedHashSet();
        Set<String> roleSet = Sets.newLinkedHashSet();
        for (Role role : roleList) {
            roleIdSet.add(role.getRoleId());
            roleSet.add(role.getRole());
        }
        // 设置权限
        Set<String> permSet = Sets.newLinkedHashSet();
        List<Permission> permList = permissionService.findEnablePermsByRoleId(roleIdSet);
        for (Permission perm : permList) {
            permSet.add(perm.getPermission());
        }
        params.put(ROLE_KEY, roleSet);
        params.put(PERMISSION_KEY, permSet);
        String jwt = this.createToken(userName, params);
        long refreshTime = jwtProperties.getExpireTime() + jwtProperties.getRefreshTime();
        cache.set(key, jwt, refreshTime * 1000);
        return jwt;
    }

    /**
     * 根据用户名获取缓存中的jwt
     */
    public String getTokenCache(String userName) {
        return cache.get(this.getKey(userName));
    }

    /**
     * 根据用户名移除缓存中的jwt
     */
    public void removeTokenCache(String userName) {
        cache.del(this.getKey(userName));
    }

    /**
     * 创建token
     *
     * @param subject 主体，一般存用户名
     * @param param   参数
     * @return 加密后的token
     */
    public String createToken(String subject, Map<String, Object> param) {
        return createToken(JwtUtil.createId(), subject, param);
    }

    /**
     * 创建token
     *
     * @param id      uuid
     * @param subject 主体，一般存用户名
     * @param param   参数
     * @return 加密后的token
     */
    public String createToken(String id, String subject, Map<String, Object> param) {
        return JwtUtil.createToken(id, subject, param, jwtProperties.getDigestAlgorithm(),
                jwtProperties.getSecretKey(), jwtProperties.getExpireTime() * 1000);
    }

    /**
     * 解析成 Claims
     */
    public Claims getClaims(String token) {
        return JwtUtil.getClaims(token, jwtProperties.getSecretKey());
    }

    /**
     * 验证
     */
    public Claims verifyToken(String token)
            throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException {
        return JwtUtil.verifyToken(token, jwtProperties.getSecretKey());
    }

    /**
     * 获取jwt id
     */
    public String getId(Claims claims) {
        return JwtUtil.getId(claims);
    }

    /**
     * 获取jwt 主题
     */
    public String getSubject(Claims claims) {
        return JwtUtil.getSubject(claims);
    }

    /**
     * 获取jwt发布时间
     */
    public Date getIssuedAt(Claims claims) {
        return JwtUtil.getIssuedAt(claims);
    }

    /**
     * 获取jwt失效时间
     */
    public Date getExpiration(Claims claims) {
        return JwtUtil.getExpiration(claims);
    }

    /**
     * 获取角色
     */
    public Set<String> getRoles(Claims claims) {
        Set<String> roleSet = Sets.newLinkedHashSet();
        if (claims == null) {
            return roleSet;
        }
        Object roles = claims.get(ROLE_KEY);
        if (roles == null) {
            return roleSet;
        }
        roleSet.addAll(((Collection<String>) roles));
        return roleSet;
    }

    /**
     * 获取权限
     */
    public Set<String> getPerms(Claims claims) {
        Set<String> permSet = Sets.newLinkedHashSet();
        if (claims == null) {
            return permSet;
        }
        Object perms = claims.get(PERMISSION_KEY);
        if (perms == null) {
            return permSet;
        }
        permSet.addAll(((Collection<String>) perms));
        return permSet;
    }

    public String getJwt() {
        return getJwt(HttpContext.getRequest());
    }

    /**
     * 获取 request 中的 jwt 值
     */
    public String getJwt(ServletRequest request) {
        if (request == null) {
            return null;
        }
        String jwtHeader = jwtProperties.getHeader();
        // 先查询请求头是否有 Authorization
        String jwt = RequestResponseUtil.getHeader(request, jwtHeader);
        if (StringUtils.hasText(jwt)) {
            return jwt;
        }
        // 如果没有则查询cookie中是否有
        jwt = RequestResponseUtil.getCookie(request, jwtHeader);
        if (StringUtils.hasText(jwt)) {
            return jwt;
        }
        // 如果没有则查询请求参数中是否有
        return RequestResponseUtil.getParameter(request, jwtHeader);
    }

    /**
     * 将 jwt 写到 response 中
     *
     * @param response 请求响应
     * @param jwt      jwt
     * @param isWrite  是否要输出
     */
    public IResult<Map<String, Object>> writeJwt(ServletResponse response, String jwt, boolean isWrite) {
        if (response != null) {
            String jwtHeader = jwtProperties.getHeader();
            // 写入请求头
            RequestResponseUtil.setHeader(response, jwtHeader, jwt);
            // 写入cookie
            RequestResponseUtil.setCookie(response, jwtHeader, jwt);
            // 写入响应体
            Map<String, Object> resultMap = Maps.newHashMap();
            resultMap.put("jwt", jwt);
            ResultVo<Map<String, Object>> resultVo = ResultVo.suc(resultMap);
            if (isWrite) {
                RequestResponseUtil.responseWrite(JsonUtil.toJSONString(resultVo), response);
            }
            return resultVo;
        }
        return ResultVo.suc();
    }

    /**
     * 验证token是否失效
     *
     * @return true:过期   false:没过期
     */
    public boolean isExpired(Claims claims) {
        return JwtUtil.isExpired(claims);
    }

    private String getKey(String userName) {
        return ShiroDefaultConst.LOGIN_KEY + userName;
    }

}
